LinkedIn Outreach Platform Agentic Workflow
Validate All Applicants — LinkedIn Outreach Platform Automation Workflow
Two-pass validation: warms up candidates first (triggers security scans), then validates. Handles 1-25 candidates dynamically.
sidebutton install LinkedIn Outreach Platform Inputs
- type
- string
- description
- |
- Optional
- Pre-defined job requirements summary. If provided, skips extraction.
- required
- false
Allowed Domains
- *.linkedin.com
Step Summary
121 steps using step types: browser.clickbrowser.extractbrowser.injectCSSbrowser.injectJSbrowser.navigatebrowser.waitcontrol.ifcontrol.stopllm.generatevariable.setworkflow.call
.
Workflow Definition
YAML source for the linkedin_validate_applicants.yaml workflow. This is the complete definition executed by the SideButton MCP server when LinkedIn Outreach Platform agents run this automation.
schema_version: 1
version: "2.1.0"
last_verified: "2026-02-27"
id: linkedin_validate_applicants
title: "Validate All Applicants"
description: "Two-pass validation: warms up candidates first (triggers security scans), then validates. Handles 1-25 candidates dynamically."
category:
level: task
domain: hiring
reusable: true
params:
job_summary:
type: string
description: |
Optional: Pre-defined job requirements summary. If provided, skips extraction.
Include JOB_ID in first line for tracking: "JOB_ID: 4350796385\n..."
Add to settings.json user_contexts for persistence.
required: false
policies:
allowed_domains:
- "*.linkedin.com"
embed:
selector: "button[aria-label^='Manage job']"
position: after
when: ".hiring-applicants__list-item"
label: "AI Validate All"
result:
action: "popup"
steps:
# ================================================================
# VISUAL FEEDBACK: Inject progress styles for candidate rows
# ================================================================
- type: browser.injectCSS
id: sb-batch-validate
css: |
@keyframes sb-pulse-border {
0%, 100% { border-left-color: rgba(21, 195, 154, 0.3); }
50% { border-left-color: rgba(21, 195, 154, 1); }
}
.hiring-applicants__list-item.sb-processing {
border-left: 4px solid #15C39A !important;
animation: sb-pulse-border 1.2s ease-in-out infinite !important;
background: rgba(21, 195, 154, 0.04) !important;
transition: all 0.3s ease !important;
}
.hiring-applicants__list-item.sb-done {
border-left: 4px solid #22C55E !important;
animation: none !important;
background: rgba(34, 197, 94, 0.04) !important;
transition: all 0.3s ease !important;
}
.hiring-applicants__list-item.sb-pending {
opacity: 0.5 !important;
transition: opacity 0.3s ease !important;
}
#sb-progress-banner {
position: sticky;
top: 0;
z-index: 100;
background: linear-gradient(135deg, #15C39A 0%, #0EA87D 100%);
color: white;
padding: 8px 16px;
font-size: 13px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
border-radius: 6px;
margin-bottom: 8px;
}
#sb-progress-banner .sb-spinner {
width: 14px; height: 14px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: white;
border-radius: 50%;
animation: sb-spin 0.8s linear infinite;
}
@keyframes sb-spin {
to { transform: rotate(360deg); }
}
# Inject progress banner above the applicant list
- type: browser.injectJS
js: |
const list = document.querySelector('.hiring-applicants__list');
if (list && !document.getElementById('sb-progress-banner')) {
const banner = document.createElement('div');
banner.id = 'sb-progress-banner';
banner.innerHTML = '<div class="sb-spinner"></div><span>Preparing validation...</span>';
list.parentElement.insertBefore(banner, list);
}
# ================================================================
# PHASE 1: GET JOB REQUIREMENTS
# ================================================================
- type: llm.generate
prompt: |
Extract the numeric job ID from this LinkedIn hiring URL:
{{_url}}
Example: https://www.linkedin.com/hiring/jobs/4350796385/applicants/12345/detail/
Returns: 4350796385
Return ONLY the numeric job ID, nothing else.
as: job_id
- type: control.if
condition: "{{job_summary}} == ''"
then:
- type: workflow.call
workflow: linkedin_extract_job_requirements
params:
job_id: "{{job_id}}"
as: job_info
- type: variable.set
name: effective_job_summary
value: "{{job_info.job_summary}}"
else:
- type: variable.set
name: effective_job_summary
value: "{{job_summary}}"
- type: control.if
condition: "{{effective_job_summary}} == ''"
then:
- type: control.stop
message: "ERROR: No job requirements for job {{job_id}}. Provide job_summary param or ensure extraction succeeds."
# ================================================================
# PHASE 2: RETURN TO APPLICANTS PAGE (only if we navigated away)
# ================================================================
- type: control.if
condition: "{{job_summary}} == ''"
then:
- type: browser.navigate
url: "{{_url}}"
- type: browser.wait
selector: ".hiring-applicants__list-item"
timeout: 10000
- type: browser.wait
ms: 2000
# ================================================================
# PHASE 3: COUNT CANDIDATES ON PAGE
# ================================================================
# Extract text from all applicant list items to count them
- type: browser.extract
selector: ".hiring-applicants__list-item"
as: applicant_list_content
- type: llm.generate
prompt: |
Count how many individual applicant entries appear in this content.
Each applicant has a name and job title.
Content:
{{applicant_list_content}}
Return ONLY a number between 1 and 25, nothing else.
If you cannot determine, return 25.
Example: 17
as: candidate_count
# ================================================================
# PHASE 4: WARM-UP ALL CANDIDATES (Trigger Security Scans)
# ================================================================
# Update banner and dim pending candidates
- type: browser.injectJS
js: |
const banner = document.getElementById('sb-progress-banner');
if (banner) banner.querySelector('span').textContent = 'Warming up candidates (0/{{candidate_count}})...';
document.querySelectorAll('.hiring-applicants__list-item').forEach(el => el.classList.add('sb-pending'));
# Warm-up 1 (always)
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "1"
# Warm-up 2
- type: control.if
condition: "{{candidate_count}} >= 2"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "2"
# Warm-up 3
- type: control.if
condition: "{{candidate_count}} >= 3"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "3"
# Warm-up 4
- type: control.if
condition: "{{candidate_count}} >= 4"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "4"
# Warm-up 5
- type: control.if
condition: "{{candidate_count}} >= 5"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "5"
# Warm-up 6
- type: control.if
condition: "{{candidate_count}} >= 6"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "6"
# Warm-up 7
- type: control.if
condition: "{{candidate_count}} >= 7"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "7"
# Warm-up 8
- type: control.if
condition: "{{candidate_count}} >= 8"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "8"
# Warm-up 9
- type: control.if
condition: "{{candidate_count}} >= 9"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "9"
# Warm-up 10
- type: control.if
condition: "{{candidate_count}} >= 10"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "10"
# Warm-up 11
- type: control.if
condition: "{{candidate_count}} >= 11"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "11"
# Warm-up 12
- type: control.if
condition: "{{candidate_count}} >= 12"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "12"
# Warm-up 13
- type: control.if
condition: "{{candidate_count}} >= 13"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "13"
# Warm-up 14
- type: control.if
condition: "{{candidate_count}} >= 14"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "14"
# Warm-up 15
- type: control.if
condition: "{{candidate_count}} >= 15"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "15"
# Warm-up 16
- type: control.if
condition: "{{candidate_count}} >= 16"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "16"
# Warm-up 17
- type: control.if
condition: "{{candidate_count}} >= 17"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "17"
# Warm-up 18
- type: control.if
condition: "{{candidate_count}} >= 18"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "18"
# Warm-up 19
- type: control.if
condition: "{{candidate_count}} >= 19"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "19"
# Warm-up 20
- type: control.if
condition: "{{candidate_count}} >= 20"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "20"
# Warm-up 21
- type: control.if
condition: "{{candidate_count}} >= 21"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "21"
# Warm-up 22
- type: control.if
condition: "{{candidate_count}} >= 22"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "22"
# Warm-up 23
- type: control.if
condition: "{{candidate_count}} >= 23"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "23"
# Warm-up 24
- type: control.if
condition: "{{candidate_count}} >= 24"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "24"
# Warm-up 25
- type: control.if
condition: "{{candidate_count}} >= 25"
then:
- type: workflow.call
workflow: linkedin_warmup_candidate
params:
index: "25"
# ================================================================
# PHASE 5: RESET TO FIRST CANDIDATE
# ================================================================
- type: browser.wait
ms: 1000
# Click first candidate to reset view (this also scrolls it into view)
- type: browser.click
selector: ".hiring-applicants__list-item:nth-child(1) a"
- type: browser.wait
ms: 1500
# ================================================================
# PHASE 6: VALIDATE APPLICANTS (Conditional based on count)
# ================================================================
# Update banner for validation phase
- type: browser.injectJS
js: |
const banner = document.getElementById('sb-progress-banner');
if (banner) banner.querySelector('span').textContent = 'Validating candidates (0/{{candidate_count}})...';
document.querySelectorAll('.hiring-applicants__list-item').forEach(el => {
el.classList.remove('sb-pending');
});
# Validate 1 (always)
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "1"
job_summary: "{{effective_job_summary}}"
as: result_1
# Validate 2
- type: control.if
condition: "{{candidate_count}} >= 2"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "2"
job_summary: "{{effective_job_summary}}"
as: result_2
# Validate 3
- type: control.if
condition: "{{candidate_count}} >= 3"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "3"
job_summary: "{{effective_job_summary}}"
as: result_3
# Validate 4
- type: control.if
condition: "{{candidate_count}} >= 4"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "4"
job_summary: "{{effective_job_summary}}"
as: result_4
# Validate 5
- type: control.if
condition: "{{candidate_count}} >= 5"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "5"
job_summary: "{{effective_job_summary}}"
as: result_5
# Validate 6
- type: control.if
condition: "{{candidate_count}} >= 6"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "6"
job_summary: "{{effective_job_summary}}"
as: result_6
# Validate 7
- type: control.if
condition: "{{candidate_count}} >= 7"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "7"
job_summary: "{{effective_job_summary}}"
as: result_7
# Validate 8
- type: control.if
condition: "{{candidate_count}} >= 8"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "8"
job_summary: "{{effective_job_summary}}"
as: result_8
# Validate 9
- type: control.if
condition: "{{candidate_count}} >= 9"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "9"
job_summary: "{{effective_job_summary}}"
as: result_9
# Validate 10
- type: control.if
condition: "{{candidate_count}} >= 10"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "10"
job_summary: "{{effective_job_summary}}"
as: result_10
# Validate 11
- type: control.if
condition: "{{candidate_count}} >= 11"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "11"
job_summary: "{{effective_job_summary}}"
as: result_11
# Validate 12
- type: control.if
condition: "{{candidate_count}} >= 12"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "12"
job_summary: "{{effective_job_summary}}"
as: result_12
# Validate 13
- type: control.if
condition: "{{candidate_count}} >= 13"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "13"
job_summary: "{{effective_job_summary}}"
as: result_13
# Validate 14
- type: control.if
condition: "{{candidate_count}} >= 14"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "14"
job_summary: "{{effective_job_summary}}"
as: result_14
# Validate 15
- type: control.if
condition: "{{candidate_count}} >= 15"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "15"
job_summary: "{{effective_job_summary}}"
as: result_15
# Validate 16
- type: control.if
condition: "{{candidate_count}} >= 16"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "16"
job_summary: "{{effective_job_summary}}"
as: result_16
# Validate 17
- type: control.if
condition: "{{candidate_count}} >= 17"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "17"
job_summary: "{{effective_job_summary}}"
as: result_17
# Validate 18
- type: control.if
condition: "{{candidate_count}} >= 18"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "18"
job_summary: "{{effective_job_summary}}"
as: result_18
# Validate 19
- type: control.if
condition: "{{candidate_count}} >= 19"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "19"
job_summary: "{{effective_job_summary}}"
as: result_19
# Validate 20
- type: control.if
condition: "{{candidate_count}} >= 20"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "20"
job_summary: "{{effective_job_summary}}"
as: result_20
# Validate 21
- type: control.if
condition: "{{candidate_count}} >= 21"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "21"
job_summary: "{{effective_job_summary}}"
as: result_21
# Validate 22
- type: control.if
condition: "{{candidate_count}} >= 22"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "22"
job_summary: "{{effective_job_summary}}"
as: result_22
# Validate 23
- type: control.if
condition: "{{candidate_count}} >= 23"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "23"
job_summary: "{{effective_job_summary}}"
as: result_23
# Validate 24
- type: control.if
condition: "{{candidate_count}} >= 24"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "24"
job_summary: "{{effective_job_summary}}"
as: result_24
# Validate 25
- type: control.if
condition: "{{candidate_count}} >= 25"
then:
- type: workflow.call
workflow: linkedin_validate_single_applicant
params:
index: "25"
job_summary: "{{effective_job_summary}}"
as: result_25
# ================================================================
# PHASE 7: COMPLETION SUMMARY
# ================================================================
# Update banner to complete state
- type: browser.injectJS
js: |
const banner = document.getElementById('sb-progress-banner');
if (banner) {
banner.style.background = 'linear-gradient(135deg, #22C55E 0%, #16A34A 100%)';
banner.innerHTML = '<span>Validation complete — generating summary...</span>';
}
- type: llm.generate
prompt: |
Summarize the validation results. Count only non-empty results.
Total candidates on page: {{candidate_count}}
Results (some may be empty if fewer than 25 candidates):
1: {{result_1.rating}}
2: {{result_2.rating}}
3: {{result_3.rating}}
4: {{result_4.rating}}
5: {{result_5.rating}}
6: {{result_6.rating}}
7: {{result_7.rating}}
8: {{result_8.rating}}
9: {{result_9.rating}}
10: {{result_10.rating}}
11: {{result_11.rating}}
12: {{result_12.rating}}
13: {{result_13.rating}}
14: {{result_14.rating}}
15: {{result_15.rating}}
16: {{result_16.rating}}
17: {{result_17.rating}}
18: {{result_18.rating}}
19: {{result_19.rating}}
20: {{result_20.rating}}
21: {{result_21.rating}}
22: {{result_22.rating}}
23: {{result_23.rating}}
24: {{result_24.rating}}
25: {{result_25.rating}}
Count how many are in each category (ignore empty/undefined):
VALIDATION COMPLETE
==================
Good Fit: X applicants
Maybe: X applicants
Not a Fit: X applicants
==================
Total Processed: {{candidate_count}}
as: summary
- type: control.stop
message: "{{summary}}"
How To Run
Install the LinkedIn Outreach Platform knowledge pack into your SideButton agent, then dispatch this workflow by its ID linkedin_validate_applicants.yaml. Agents invoke it directly via the MCP protocol or through the portal.